package com.utility.project.common.config.mybatisplus;

import java.util.Date;
import java.util.UUID;

/**
 * 
 * 生成长度为16位的GUID. JS的Long最大值2^53-1,而Java的Long最大值2^63-1. 默认雪花算法生成ID是19位,会导致精度丢失问题. 这里生成16位长度的GUID,范围和js保持一致.
 * 参照https://blog.csdn.net/Be_insighted/article/details/122355017 compressed id generator, result id not great than
 * 53bits before 2318-06-04.
 * 
 * @author zouke
 * @date 2023/1/29 16:18
 * @description
 */
public class IdGenerator {

    private static IdGenerator instance = new IdGenerator(0);

    public static IdGenerator initDefaultInstance(int machineId) {
        instance = new IdGenerator(machineId);
        return instance;
    }

    public static IdGenerator getInstance() {
        return instance;
    }

    public static long generateId() {
        return instance.nextId();
    }

    /***
     * max 31
     */
    private final static long MACHINE_BIT = 5;
    /***
     * 128/1ms
     */
    private final static long SEQUENCE_BIT = 7;

    /**
     * mask/max value x位2进制数表示的最大数值 ~表示取反码
     */
    private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);

    /**
     * 2023年1月1日的时间毫秒
     */
    private final static long CURRENT_DATE_2023_01_01 = 1672502400000L;


    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long TIMESTMP_LEFT = MACHINE_BIT + SEQUENCE_BIT;

    private long machineId;
    private long sequence = 0L;
    private long lastStmp = -1L;

    private IdGenerator(long machineId) {
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException(
                "machineId can't be greater than " + MAX_MACHINE_NUM + " or less than 0");
        }
        this.machineId = machineId;
    }

    /**
     * generate new ID
     *
     */
    public synchronized long nextId() {
        long currStmp = getTimestamp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currStmp == lastStmp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                currStmp = getNextTimestamp();
            }
        } else {
            sequence = 0L;
        }

        lastStmp = currStmp;

        return ((currStmp << TIMESTMP_LEFT) - CURRENT_DATE_2023_01_01) | machineId << MACHINE_LEFT | sequence;
    }

    private long getNextTimestamp() {
        long mill = getTimestamp();
        while (mill <= lastStmp) {
            mill = getTimestamp();
        }
        return mill;
    }

    private long getTimestamp() {
        // per 1ms
        return System.currentTimeMillis();
    }

    public static Date parseIdTimestamp(long id) {
        return new Date((id >>> TIMESTMP_LEFT));
    }

    public static String uuid() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

}
